package Datastructure;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;





/**
 * this class is used to convert the input file into an array,
 * that is later used to create the nodes and edges for the graph
 */
public class Converter {
	
	//needed for reading and writing
	ArrayList<String> rowNames=new ArrayList<String>();
	public ArrayList<String> colNames=new ArrayList<String>();
	
	
	/**
	 * converts the given file into a matrix
	 * c^T numOfColumns
	 * A
	 * (l)
	 * (u)
	 * b
	 * s
	 * with the dimensions:
	 * s and b: (m,1)
	 * A: (m,n)
	 * c,l,u: (n,1)
	 * @return a matrix
	 */
	public ArrayList<ArrayList<Entry>> convert(String filename){
		File f= new File(filename);
		
		try{
			FileReader fr = new FileReader(f);
			BufferedReader br = new BufferedReader(fr);
			
			
			
			ArrayList<ArrayList<Entry>> matrix=new ArrayList<ArrayList<Entry>>();
			int numOfRows=0;
			int numOfColumns=0;
			
			ArrayList<Entry> signum=new ArrayList<Entry>();//what kind of equality (=, <=, >=)
			ArrayList<Entry> b=new ArrayList<Entry>();
			
			String colStr="";//save name of the column (to count them)
			
			int counter=0;//for upper and lower bound
			
			/*
			 * mode=1 => ROWS
			 * mode=2 => COLUMNS
			 * mode=3 => RHS
			 * mode=4 => BOUNDS
			 */
			int mode=0;
			
			while(br.ready()){
				String str=br.readLine();
				if(str.length()==0)continue;
				if (str.equals("ROWS")){mode=1;continue;}
				else if(mode==0)
					continue;
				if (str.equals("COLUMNS")){mode=2;continue;}
				if(str.equals("RHS")){
					mode=3;
					counter=0;
					continue;
				}
				if(str.equals("BOUNDS")){
					mode=4;
					matrix.add(new ArrayList<Entry>());//for lower bounds
					matrix.add(new ArrayList<Entry>());//for upper bounds
					continue;
				}
				
				if(str.equals("ENDATA")){
					matrix.get(0).add(new Entry(numOfColumns,Double.NEGATIVE_INFINITY));
					matrix.add(b);
					matrix.add(signum);
					//System.out.println(signum.size());
					//System.out.println();
					break;
				}
				
				//line represents a row, but is not "ROWS"
				if(mode==1 && !str.equals("ROWS") && str.length()>0){
					
					String[] tmp=reduce(str.split(" "));
					rowNames.add(tmp[1]);
					
					matrix.add(new ArrayList<Entry>());
					
					switch (tmp[0]){
					case "N": signum.add(new Entry(numOfRows,2));break;
					case "E": signum.add(new Entry(numOfRows,0));break;//==
					case "L": signum.add(new Entry(numOfRows,-1));break;//<=
					case "G": signum.add(new Entry(numOfRows,1));break;//>=
					
					}
					numOfRows++;
					continue;
				}
				
				
				if(mode==2 && !str.equals("COLUMNS") && str.length()>0){
					
					String[] split=reduce(str.split(" "));
					String name=split[0];
					
					if(!colStr.equals(name)){
						numOfColumns++;
						colStr=name;
						colNames.add(name);
					}
					
					for(int i=1;i<split.length;i+=2){
						String varName=split[i];
						double val=0;
						try{
							val=Double.parseDouble(split[i+1]);
						}
						catch(NumberFormatException e){
							String[] sVal=split[i+1].split("/");
							val=(double) Double.parseDouble(sVal[0])/Double.parseDouble(sVal[1]);
						}
						
						
						for(int j=0;j<rowNames.size();j++){
							
							if(rowNames.get(j).equals(varName)){
								matrix.get(j).add(new Entry(numOfColumns-1,val));
							}
						}
						
					}
					
					continue;
				}
				
				
				//System.out.println(str);
				/*if(low==null)
					low=new double[numOfColumns];
				if(up==null){
					up=new double[numOfColumns];
					for(int i=0;i<up.length;i++){
						up[i]=Double.POSITIVE_INFINITY;
					}
				}*/
				
				if(mode==3 && !str.equals("RHS") && str.length()>0){
					String[] split=reduce(str.split(" "));
					
					for(int i=1;i<split.length;i+=2){
						String rhsName=split[i];
						double val=0;
						try{
							val=Double.parseDouble(split[i+1]);
						}
						catch(NumberFormatException e){
							String[] sVal=split[i+1].split("/");
							val=(double) Double.parseDouble(sVal[0])/Double.parseDouble(sVal[1]);
						}
						for(int j=0;j<rowNames.size();j++){
							
							if(rowNames.get(j).equals(rhsName)){
								b.add(new Entry(j-1,val));
								//matrix.get(j).add(new Entry(numOfColumns, val));
								//rhs[j]=val;
							}
						}
						
					}
					
					continue;
				}
				if(mode==4 && !str.equals("BOUNDS") && str.length()>0){
					//TODO verbessern, Grenzen direkt in die Matrix
					
					/*
					 * 
					 */
					String[] split=reduce(str.split(" "));
					
					String kind=split[0];
					for(int i=2;i<split.length;i+=2){
						String boundName=split[i];
						double val=0;
						try{
							val=Double.parseDouble(split[i+1]);
						}
						catch(NumberFormatException e){
							String[] sVal=split[i+1].split("/");
							val=(double) Double.parseDouble(sVal[0])/Double.parseDouble(sVal[1]);
						}
						for(int j=0;j<colNames.size();j++){
							
							if(colNames.get(j).equals(boundName)){
								
								if(kind.equals("LO")){//x>=u <=> -x<=-u
									matrix.add(new ArrayList<Entry>());
									matrix.get(matrix.size()-1).add(new Entry(j,-1));
									b.add(new Entry(numOfRows+counter, -val));//-val um schon zu normieren (x>=l => -x<=-l)
									signum.add(new Entry(numOfRows+counter,-1));
									counter++;
								}
									//low[j]=val;
								if(kind.equals("UP")){//x<=u
									matrix.add(new ArrayList<Entry>());
									matrix.get(matrix.size()-1).add(new Entry(j,1));
									b.add(new Entry(numOfRows+counter, val));
									signum.add(new Entry(numOfRows+counter,-1));
									counter++;
								}
								if(kind.equals("FX")){//x==u
									matrix.add(new ArrayList<Entry>());
									matrix.get(matrix.size()-1).add(new Entry(j,1));
									b.add(new Entry(numOfRows+counter, val));
									signum.add(new Entry(numOfRows,0));
									counter++;
								}
							}
						}
					}
					
					continue;
				}
				
			}
			for(int i=0;i<numOfRows;i++){
				colNames.add("Schlupf"+i);
			}
			System.out.println("nORows: "+numOfRows);
			System.out.println("nOCols: "+numOfColumns);
			
			
			return matrix;
		}
		catch (FileNotFoundException e) {
			System.err.println("File "+filename+" not found!");
			return null;
		}
		catch (IOException e){
			System.err.println("Error while reading: "+filename+"!");
			return null;
		}
		catch(NullPointerException e){
			System.out.println("ende");
		}
		
		return null;
	}
	
	public String[] reduce(String[] s){
		int num=0;
		for(int i=0; i<s.length;i++){
			if(s[i].length()>0)
				num++;
		}
		
		String[] s_red=new String[num];
		int counter=0;
		
		for(int i=0;i<s.length;i++){
			if(s[i].length()>0){
				s_red[counter]=s[i];
				counter++;
			}
		}
		
		return s_red;
	}
	
	/**
	 * writes the output file
	 * @param filename address of the output file
	 * @param xB vector of the base-values (xN=0)
	 * @param base base of the solution, used to find the names
	 */
	public void write(String filename, ArrayList<Entry> xB, int[] base){
		/*
		if(B.length!=xB.length){
			System.out.println("ERROR: length of B and xB don't match");
			return;
		}
		*/
		try {
			FileWriter writer= new FileWriter(filename,true);
			
			/*for(int i=0;i<xB.size();i++){
				System.out.println("xB "+xB.size());
				System.out.println("xBi "+xB.get(i).getPosition()+" | "+xB.get(i).getValue());
			}*/
			
			for(int i=0;i<xB.size();i++){
				//System.out.println("xB "+xB.size());
				//System.out.println("colNames danach "+this.colNames.size());
				//System.out.println("xb, base"+xB.size()+", "+base.length);
				String str=colNames.get(base[xB.get(i).getPosition()])+" "+xB.get(i).getValue()+"\n";
				writer.write(str);
				writer.flush();
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * writes the outputfile
	 * @param filename adress of the output file
	 * @param g graph
	 */
	/*public void writeFile(String filename, Graph g){
		
		try {
			FileWriter writer= new FileWriter(filename,true);
			ArrayList<GraphEdge> edges=new ArrayList<GraphEdge>();
		
			// creates a list of edges
			for(int i=0;i<g.getNumOfNodes();i++){
				GraphNode node=g.getNode(i);
				for(int j=0;j<node.getOutgoingCount();j++){
					edges.add(node.getOutgoing(j));
					
				}
			}
			
			//write edge data: "start node" "end node" "flow"
			for(int i=0;i<edges.size();i++){
				GraphEdge edge=edges.get(i);
				
				//only write the original edges
				if(!edge.isOriginal())
					continue;
				
				int v=edge.getStart().getId();
				int w=edge.getEnd().getId();
				double flow=edge.getFlow();
				String str=Integer.toString(v)+" "+Integer.toString(w)+" "+Double.toString(flow)+"\n";
				
				writer.write(str);
				writer.flush();
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	*/
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		System.out.println("Fange an");
		if(args.length==0){
			System.out.println("Please enter one filename");
			return;
		}
		Converter converter=new Converter();
		
		System.out.println("Convert "+args[0]);
		ArrayList<ArrayList<Entry>> matrix=converter.convert(args[0]);
		// hier und evtl. auch woanders beachten, ob es obere und untere Schranken gibt
		for(int i=0;i<matrix.size()-2;i++){
			ArrayList<Entry> entries=matrix.get(i);
			
			String out="";
			for(int j=0;j<entries.size();j++){
				out+=entries.get(j).getValue()+" ";
			}
			if(i>0){
				ArrayList<Entry> tmp1=matrix.get(matrix.size()-1);
				for(int k=0;k<tmp1.size();k++){
					if(i==tmp1.get(k).getPosition())
						switch((int)matrix.get(matrix.size()-1).get(k).getValue()){
						case 0: out+=" = ";break;
						case 1: out+=" >=";break;
						case -1: out+="<=";break;
						}
				}
				
				ArrayList<Entry> tmp=matrix.get(matrix.size()-2);
				for(int k=0;k<tmp.size();k++){
					if(i==tmp.get(k).getPosition()+1)
						out+=matrix.get(matrix.size()-2).get(k).getValue()+" ";
				}
				
				
			}
			System.out.println(out);
		}
		
		/*for(int i=0;i<matrix[0].length;i++){
			String out="";
			for(int j=0;j<matrix.length;j++){
				if(i==0 && j>=matrix.length-2)
					break;
				if(j==matrix.length-2){
					switch((int)matrix[j][i]){
					case 0: out+=" = ";break;
					case 1: out+=" >=";break;
					case -1: out+="<=";break;
					}
					continue;
				}
				
				
				out+=matrix[j][i]+" ";
			}
			System.out.println(out);
		}*/
		
		System.out.println("Ende");
	}

}
